#include "depui/depui.h"

#ifdef MX_PLATFORM_ALLEGRO

#   define MXMODULE_DRS

#   include <allegro.h>

static int mx__allegro_bufferx, mx__allegro_buffery;
static BITMAP *mx__allegro_buffer = 0;

static MX_PLATFORM_FONT mx__allegro_font[] = {
	{0, "Builtin 8x8"},
};

#   define MX__ARRAY(a) (sizeof(a)/sizeof(a[0]))

const MX_PLATFORM_FONT *mx_platform_font_system(const int i)
{
	if (i < 0)
		return &mx__allegro_font[0];

	if (i < (int) MX__ARRAY(mx__allegro_font))
		return &mx__allegro_font[i];

	return 0;
}

void *mx_platform_font_load(const char *filename)
{
	PALETTE pal;

	return load_font(filename, pal, 0);
}

const char *mx_platform_font_path(void)
{
	return "*.fnt;*.pcx";
}

void mx_platform_font_free(void *pfont)
{
	FONT *f = (FONT *) pfont;

	destroy_font(f);
}

unsigned int mx_platform_font_height(void *pfont)
{
	FONT *f;

	if (!pfont)
		pfont = mx__allegro_font[0].font;

	f = (FONT *) pfont;

	return text_height(f);
}

/**todo Allegros font witdth should be able to take more than 1024 chars */
unsigned int mx_platform_font_width(void *pfont, const char *text, int len)
{
	unsigned int ret;
	FONT *f;
	char *temp = (char *) text;

	if (!font)
		font = mx__allegro_font[0].font;

	f = (FONT *) pfont;

	if (len >= 0) {
		temp = malloc(1024);

		strncpy(temp, text, 1023);
		temp[1023] = 0;

		if (len < 1023)
			temp[len] = 0;
	}

	ret = text_length(f, temp);

	if (temp != text)
		free(temp);

	return ret;
}

static const MX_PLATFORM_RESOLUTION mx__allegro_resolutions[] = {
	{640, 480, "640x480"},
	{800, 600, "800x600"},
	{1024, 768, "1024x768"},
	{1280, 1024, "1280x1024"},
	{0, 0, 0}
};

static const MX_PLATFORM_DEPTH mx__allegro_depths[] = {
	{8, "8"},
	{16, "16"},
	{24, "24"},
	{32, "32"},
	{0, 0}
};

static int mx___allegro_drivers[] = {
	GFX_AUTODETECT,
	GFX_AUTODETECT_FULLSCREEN,
	GFX_AUTODETECT_WINDOWED
};

struct MX_PLATFORM_DRIVER mx__allegro_drivers[] = {
	{&mx___allegro_drivers[0], "auto"},
	{&mx___allegro_drivers[1], "fullscreen"},
	{&mx___allegro_drivers[2], "windowed"},
	{0, 0}
};

extern const MX_THEME mx_theme_allegro;

static MX_PLATFORM_THEME mx__allegro_themes[] = {
	{&mx_theme_allegro, "allegro theme"},
	{0, 0},
};

void mx_platform_modes(const MX_PLATFORM_DRIVER * d[], const MX_PLATFORM_RESOLUTION * r[], const MX_PLATFORM_DEPTH * c[],
					   const MX_PLATFORM_THEME * t[])
{
	*d = mx__allegro_drivers;
	*r = mx__allegro_resolutions;
	*c = mx__allegro_depths;
	*t = mx__allegro_themes;
}

static int mx__allegro_started = 0;

unsigned mx_platform_start(int w, int h, int c, const void *driver)
{
	int card = GFX_AUTODETECT;
	int oldw = 640, oldh = 480, oldc = 16;

	if (driver)
		card = *(const int *) driver;

	if (!mx__allegro_started) {
		if (allegro_init() != 0)
			return false;

		install_keyboard();
		install_timer();
		install_mouse();
		mx__allegro_font[0].font = font;

		mx__allegro_started = 1;
	} else {
		oldw = SCREEN_W;
		oldh = SCREEN_H;
		oldc = get_color_depth();

		show_mouse(NULL);
	}

	if (w == 0)
		w = 640;
	if (h == 0)
		h = 480;
	if (c == 0)
		c = 16;

	set_color_depth(c);
	if (set_gfx_mode(card, w, h, 0, 0) != 0) {

		set_color_depth(oldc);
		if (set_gfx_mode(GFX_AUTODETECT, oldw, oldh, 0, 0) != 0)
			return false;
	}

	mx_drs_area(w - 1, h - 1);

	show_mouse(screen);

	return true;
}

void mx_platform_stop(void)
{
	remove_keyboard();
	allegro_exit();
}

unsigned mx_platform_clip(const MX_RECT * r)
{
	set_clip_rect(mx__allegro_buffer, mx__allegro_bufferx + r->x1, mx__allegro_buffery + r->y1, mx__allegro_bufferx + r->x2,
				  mx__allegro_buffery + r->y2);
	return true;
}

const MX_RECT *mx_platform_rect(void)
{
	static MX_RECT rrect;

	rrect.x1 = 0;
	rrect.y1 = 0;
	rrect.x2 = SCREEN_W;
	rrect.y2 = SCREEN_H;

	return &rrect;
}

unsigned mx_platform_pointer(int *px, int *py, int *pb)
{
	*px = mouse_x;
	*py = mouse_y;
	*pb = mouse_b;

	return true;
}

unsigned mx_platform_key(int *scan, int *ascii)
{
	int val;

	if (!keypressed())
		return false;

	val = readkey();
	*scan = (val >> 8) & 0xff;
	*ascii = scancode_to_ascii(*scan);

	return true;
}

static void _flush(MX_RECT * rrect)
{
	MX_RECT brect;

	brect.x1 = rrect->x1;
	brect.y1 = rrect->y1;
	brect.x2 = rrect->x1 + mx__allegro_buffer->w - 1;
	brect.y2 = rrect->y1 + mx__allegro_buffer->h - 1;

	MXRECT_INTERSECT(brect, *rrect, *rrect);

	mx__allegro_bufferx = -rrect->x1;
	mx__allegro_buffery = -rrect->y1;
	mx__gui_redraw(rrect);

	scare_mouse();

	acquire_screen();
	blit(mx__allegro_buffer, screen, 0, 0, -mx__allegro_bufferx, -mx__allegro_buffery, rrect->x2 - rrect->x1 + 1, rrect->y2 - rrect->y1 + 1);
	release_screen();

	unscare_mouse();
}

unsigned mx_platform_poll(void)
{
	mx_drs_update(_flush);

	poll_mouse();
	poll_keyboard();

	return true;
}

void mx_platform_dirty(const MX_RECT * rrect)
{
	mx_drs_dirty(rrect, true);
}

static unsigned mx_theme_allegro_start(void)
{
	assert(mx__allegro_buffer == 0);
	mx__allegro_buffer = create_bitmap(256, 128);
	mx__allegro_bufferx = 0;
	mx__allegro_buffery = 0;
	assert(mx__allegro_buffer);
	return true;
}

static void mx_theme_allegro_stop(void)
{
	assert(mx__allegro_buffer);
	destroy_bitmap(mx__allegro_buffer);
	mx__allegro_buffer = 0;
}

static void mx_theme_allegro_event(void)
{
	mx__event_default();

	if ((mx.event == MX_DEFAULTRECT) && (MXID(mx.obj) == MXID_WINRESIZE)) {
		MX_RECT *rect = mx_defaultrect_data();

		rect->y2 = rect->y1 + (rect->y2 - rect->y1) / 2;
	}
}

static void mx__allegro_frame(const MX_RECT * objrect, int edge, int fill)
{
	hline(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2, edge);
	hline(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y2, mx__allegro_bufferx + objrect->x2, edge);
	vline(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1 + 1, mx__allegro_buffery + objrect->y2 - 1, edge);
	vline(mx__allegro_buffer, mx__allegro_bufferx + objrect->x2, mx__allegro_buffery + objrect->y1 + 1, mx__allegro_buffery + objrect->y2 - 1, edge);

	rectfill(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1 + 1, mx__allegro_buffery + objrect->y1 + 1, mx__allegro_bufferx + objrect->x2 - 1,
			 mx__allegro_buffery + objrect->y2 - 1, fill);
}

static void mx_theme_allegro_obj(MX_OBJ * obj)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(obj);

		rectfill(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(255, 255, 255));
	}
}

static void mx_theme_allegro_vslider(MX_SLIDER * slider)
{
	if (mx_exposing()) {
		int fill = makecol(255, 255, 255);
		MX_RECT objrect = *MXRECT(slider);

		mx__allegro_frame(&objrect, makecol(64, 64, 64), makecol(164, 164, 164));

		if (mx_armed(slider))
			fill = makecol(0, 164, 0);

		objrect.y2 = objrect.y1 + slider->base.slider._lower;
		objrect.y1 += slider->base.slider._upper;

		mx__allegro_frame(&objrect, makecol(0, 0, 0), fill);
	}
}

static void mx_theme_allegro_hslider(MX_SLIDER * slider)
{
	if (mx_exposing()) {
		int fill = makecol(255, 255, 255);
		MX_RECT objrect = *MXRECT(slider);

		mx__allegro_frame(&objrect, makecol(64, 64, 64), makecol(164, 164, 164));

		if (mx_armed(slider))
			fill = makecol(0, 164, 0);

		objrect.x2 = objrect.x1 + slider->base.slider._lower;
		objrect.x1 += slider->base.slider._upper;

		mx__allegro_frame(&objrect, makecol(0, 0, 0), fill);
	}
}

static void mx__textchunks(const MX_TEXTUAL_DATA * textual, int fore, int x, int y)
{
	int offx = 0, offy = 0;
	char *temptext = 0;
	const int x1 = mx_x1(textual);
	const int y1 = mx_y1(textual);

	MX__TEXTCHUNK *chunk = mx_dllist_first(textual);

	/* Left and top alignment is correct with offset 0 */
	if (textual->_align & MX_ALIGN_RIGHT)
		offx = mx_w(textual) - textual->_textwidth - 2 * x;
	if (textual->_align & MX_ALIGN_HCENTER)
		offx = (mx_w(textual) - textual->_textwidth - 2 * x) / 2;

	if (textual->_align & MX_ALIGN_BOTTOM)
		offy = mx_h(textual) - textual->_textheight - 2 * y;
	if (textual->_align & MX_ALIGN_VCENTER)
		offy = (mx_h(textual) - textual->_textheight - 2 * y) / 2;

	while (chunk) {
		int len = 0;
		const char *text = mx_string_text(&chunk->text, &len);

		if (chunk->cursor) {
			int fill = makecol(164, 164, 164);

			if (mx_focused(textual))
				fill = makecol(0, 164, 0);

			rectfill(mx__allegro_buffer, mx__allegro_bufferx + x + x1 + chunk->rect.x1, mx__allegro_buffery + y + y1 + chunk->rect.y1,
					 mx__allegro_bufferx + x + x1 + chunk->rect.x2, mx__allegro_buffery + y + y1 + chunk->rect.y2, fill);
		}

		if (text) {
			const char *t = text;
			FONT *f = (FONT *) mx__textual_platform_font(textual);

			if (!temptext)
				temptext = malloc(1024);

			if (len >= 0) {
				strncpy(temptext, text, 1023);
				temptext[1023] = 0;

				if (len < 1023)
					temptext[len] = 0;

				t = temptext;
			}

			textout_ex(mx__allegro_buffer, f, t, mx__allegro_bufferx + x + x1 + chunk->rect.x1 + offx,
					   mx__allegro_buffery + y + y1 + chunk->rect.y1 + offy, fore, -1);
		}
		chunk = mx_dllist_next(chunk);
	}
	free(temptext);
}

static int mx__textback(const MX_TEXTUAL_DATA * textual, int x1, int y1, int x2, int y2, unsigned armable)
{
	int fore = makecol(0, 0, 0);
	int back = makecol(255, 255, 255);

	if (mx_selected(textual)) {
		back = makecol(0, 164, 0);
		fore = makecol(255, 255, 255);
	}

	if (mx_disabled(textual))
		fore = makecol(64, 64, 64);

	if ((armable) && (mx_armed(textual)))
		back = makecol(164, 164, 164);

	rectfill(mx__allegro_buffer, mx__allegro_bufferx + x1, mx__allegro_buffery + y1, mx__allegro_bufferx + x2, mx__allegro_buffery + y2, back);

	return fore;
}

static void mx_theme_allegro_textual(MX_TEXTUAL * text)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(text);
		int fore = mx__textback(MXTEXTUAL(text), objrect->x1, objrect->y1,
								objrect->x2, objrect->y2, false);

		mx__textchunks(MXTEXTUAL(text), fore, 0, 0);
	}
}

static void mx_theme_allegro_scrollcorner(MX_OBJ * scrollcorner)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(scrollcorner);

		rectfill(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(164, 164, 164));
	}
}

static void mx_theme_allegro_scroll(MX_SCROLL * scroll)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(scroll);

		rectfill(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(255, 255, 255));
	}
}

static void mx_theme_allegro_button(MX_BUTTON * button)
{
	if (mx_exposing()) {
		int fore;
		const MX_RECT *objrect = MXRECT(button);
		int offset = 0;

		if (mx_selected(button))
			offset = 1;

		if (mx_armed(button))
			rect(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(255, 64, 64));

		else if (mx_selected(button))
			rect(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(255, 255, 255));

		else
			rect(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(64, 64, 64));

		fore = mx__textback(MXTEXTUAL(button), objrect->x1 + 1, objrect->y1 + 1, objrect->x2 - 1, objrect->y2 - 1, true);

		mx__textchunks(MXTEXTUAL(button), fore, 2 + offset, 2 + offset);
	}
}

void mx_theme_allegro_listelem(MX_LISTELEM * listelem)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(listelem);

		int fore = mx__textback(MXTEXTUAL(listelem), objrect->x1, objrect->y1,
								objrect->x2, objrect->y2, true);

		mx__textchunks(MXTEXTUAL(listelem), fore, 0, 0);
	}
}

static void mx_theme_allegro_root(MX_WIN * root)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(root);

		rectfill(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(108, 166, 205));
	}
}

static void mx_theme_allegro_winborder(MX_WINBORDER * border)
{
	if (mx_exposing()) {
		MX_WIN *owner = MXOBJ(border)->_win;
		const MX_RECT *objrect = MXRECT(border);
		int fore = makecol(0, 0, 0);
		int fill = makecol(255, 255, 255);

		if (mx_active(owner))
			fill = makecol(255, 250, 205);

		if (mx_disabled(owner))
			fore = makecol(164, 164, 164);

		mx__allegro_frame(objrect, makecol(0, 0, 0), fill);

		if (owner)
			mx__textchunks(MXTEXTUAL(owner), fore, mx_x(border) - mx_x(owner) + 2, mx_y(border) - mx_y(owner) + 2);
	}
}

static void mx_theme_allegro_win(MX_WIN * win)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(win);

		rectfill(mx__allegro_buffer, mx__allegro_bufferx + objrect->x1, mx__allegro_buffery + objrect->y1, mx__allegro_bufferx + objrect->x2,
				 mx__allegro_buffery + objrect->y2, makecol(164, 164, 164));
	}
}

const MX_THEME mx_theme_allegro = {
	mx_theme_allegro_start,
	mx_theme_allegro_stop,
	mx_theme_allegro_event,
	mx_theme_allegro_obj,
	mx_theme_allegro_vslider,
	mx_theme_allegro_hslider,
	mx_theme_allegro_textual,
	mx_theme_allegro_scrollcorner,
	mx_theme_allegro_scroll,
	mx_theme_allegro_button,
	mx_theme_allegro_listelem,
	mx_theme_allegro_root,
	mx_theme_allegro_winborder,
	mx_theme_allegro_win
};

#endif
